package com.github.prontera.common.filter.limit;

import java.util.HashMap;
import java.util.Map;

public class LimiterStatsService {

	private Map<String, LimiterStats> limiterMap = new HashMap<String, LimiterStats>();

	private static LimiterStatsService limiterStatsService;

	private static Integer limitDefault = 1;

	private static LimiterCallback limiterCallbackDefault = new LimiterCallback() {

		@Override
		public void noexecute(LimiterContext limiterContext) {

		}

		@Override
		public void execute(LimiterContext limiterContext) {

		}
	};

	public interface LimiterCallback {

		void noexecute(LimiterContext limiterContext);

		void execute(LimiterContext limiterContext);

	}

	public static class LimiterStats {

		private long timestamp;
		private String key;
		private int requests;

		public long getTimestamp() {
			return timestamp;
		}

		public String getKey() {
			return key;
		}

		public int getRequests() {
			return requests;
		}

		public LimiterStats setTimestamp(long timestamp) {
			this.timestamp = timestamp;
			return this;
		}

		public LimiterStats setKey(String key) {
			this.key = key;
			return this;
		}

		public LimiterStats setRequests(int requests) {
			this.requests = requests;
			return this;
		}

	}

	public static class LimiterContext {

		private Integer limit;
		private LimiterStats stats;

		public Integer getLimit() {
			return limit;
		}

		public void setLimit(Integer limit) {
			this.limit = limit;
		}

		public LimiterStats getStats() {
			return stats;
		}

		public void setStats(LimiterStats stats) {
			this.stats = stats;
		}

	}

	public static LimiterStatsService getInstance() {
		synchronized (LimiterStatsService.class) {
			if (limiterStatsService == null) {
				limiterStatsService = new LimiterStatsService();
			}
		}
		return limiterStatsService;
	}

	/**
	 * 每秒钟限制执行次数
	 * 
	 * @param key
	 * @param limit
	 *            次数
	 * @param seconds
	 *            几秒钟以内
	 * @param limiterCallback
	 *            执行回调
	 */
	public void execute(String key, Integer limit, Integer seconds, LimiterCallback limiterCallback) {
		if (limiterCallback == null)
			limiterCallback = limiterCallbackDefault;
		if (limit == null)
			limit = limitDefault;
		final long timestamp = (System.currentTimeMillis() / 1000l) * ((seconds == null || seconds < 0) ? 1 * 1000l : seconds * 1000l);
		LimiterStats stats = limiterMap.get(key);
		if (stats == null || (stats.getTimestamp()) < timestamp) {
			stats = new LimiterStats().setTimestamp(timestamp).setKey(key).setRequests(0);
			limiterMap.put(key, stats);
		}
		LimiterContext limiterContext = new LimiterContext();
		limiterContext.setLimit(stats.getRequests());
		limiterContext.setStats(stats);
		if (stats.getRequests() >= limit) {
			limiterCallback.noexecute(limiterContext);
		} else {
			stats.setRequests(stats.getRequests() + 1);
			limiterMap.put(stats.getKey(), stats);
			limiterCallback.execute(limiterContext);
		}
	}

}